// This component is essential for most projects, providing basic system operations and commonly used utility functions. Additionally, dxLogger also depends on this component.

import { commonClass } from './libvbar-m-dxcommon.so'
import dxMap from './dxMap.js'
import * as std from 'std';
import * as os from "os"

const commonObj = new commonClass();

const common = {}
/**
 * Get the system startup time (in seconds).
 * @returns 
 */
common.getUptime = function () {
    return commonObj.getUptime();
}

/**
 * Get the total system memory (in bytes).
 * @returns 
 */
common.getTotalmem = function () {
    return commonObj.getTotalmem();
}

/**
 * Get the system's remaining memory (in bytes).
 * @returns 
 */
common.getFreemem = function () {
    return commonObj.getFreemem();
}

/**
 * Deprcated, Please use `common.logMemory` instead.
 */
common.logMem = function (logger, interval = 10) {
    if (logger) {
        logger.debug("Deprcated, Please use `common.logMemory` instead.")
    }
}
/**
 * Logs the current memory usage at regular intervals for debugging and monitoring purposes.
 * @param {object} logger The dxLogger module object.
 * @param {number} interval The logging interval in seconds, defaulting to 10 seconds.
 */
common.logMemory = function (logger, interval = 10) {
    if (!logger) return;

    let first = new Date().getTime();
    let min = common.getFreemem() / 1024;
    let max = min;
    const _logmemory = () => {
        try {
            const now = new Date().getTime();
            const pass = (now - first) / 1000;
            const free = common.getFreemem() / 1024;

            min = Math.min(min, free);
            max = Math.max(max, free);

            // Format time
            let passStr;
            if (pass > 1700000000) {
                first = now;
                passStr = 'time synced, 0s';
            } else if (pass >= 3600) {
                const hours = Math.floor(pass / 3600);
                const minutes = Math.floor((pass % 3600) / 60);
                const seconds = Math.floor(pass % 60);
                passStr = `${hours}h ${minutes}m ${seconds}s`;
            } else if (pass >= 60) {
                const minutes = Math.floor(pass / 60);
                const seconds = Math.floor(pass % 60);
                passStr = `${minutes}m ${seconds}s`;
            } else {
                passStr = `${Math.floor(pass)}s`;
            }
            const log = `------ ${passStr} passed, free memory (k): ${free}, min free memory (k): ${min}, max free memory (k): ${max} ------`;
            logger.info(log);
        } catch (err) {
            logger.error('Error in logMemory:', err);
        } finally {
            os.setTimeout(_logmemory, interval * 1000);
        }
    };

    os.setTimeout(_logmemory, interval * 1000);
};

/**
 * The principle of converting asynchronous to synchronous is as follows: 
 * the `request` function periodically checks a designated variable in memory for a value. 
 * If the value is found within the timeout period, the result is returned; otherwise, 
 * it is considered a timeout. The `response` function is responsible for storing the result
 *  in the designated variable once the asynchronous request is completed.
 */
common.sync = {
    /**
     * Block and wait for data
     * Usage:
        common.sync.request(topic, 200)
        .then((data) => {
            log.info("Received data:", data);
        })
        .catch((err) => {
            log.error("Request failed:", err.message);
        });
     * @param {string} topic The unique identifier for each request
     * @param {int} timeout waitting timeout(microsecond)，default is 200 ms
     * @returns 
     */
    request: function (topic, timeout = 200) {
        return new Promise((resolve, reject) => {
            let map = dxMap.get("SYNC");
            let startTime = Date.now();

            const checkData = () => {
                let data = map.get(topic);
                if (data) {
                    map.del(topic); //del data in map
                    resolve(data); //return data
                } else if (Date.now() - startTime >= timeout) {
                    map.del(topic); // del data in map with timeout
                    reject(new Error(`Timeout exceeded for topic: ${topic}`)); 
                } else {
                    os.setTimeout(checkData, 10); //every 10 ms to check
                }
            };

            os.setTimeout(checkData, 10); // first check
        });
    },

    /**
     * notify data to requester
     * @param {string} topic The unique identifier for each request
     * @param {*} data 
     * @returns 
     */
    response: function (topic, data) {
        let map = dxMap.get("SYNC");
        map.put(topic, data); // save data in map
    }
};

/**
 * Get the total available disk space of the system (in bytes).
 * @param {string} path Different disk partition names (not directory names), optional, defaults to '/'
 */
common.getTotaldisk = function (path) {
    return commonObj.getTotaldisk(!path ? "/" : path);
}

/**
 * Get the remaining available disk space on the system (in bytes).
 * @param {string} path Different disk partition names (not directory names), optional, defaults to '/'
 * @returns 
 */
common.getFreedisk = function (path) {
    return commonObj.getFreedisk(!path ? "/" : path);
}

/**
 * Get CPU ID
 * @param {number} len Optional, default length is 33 characters.
 * @returns 
 */
common.getCpuid = function () {
    return commonObj.getCpuid(33);
}

/**
 * Get the device UUID (string).
 * @returns 
 */
common.getUuid = function () {
    return commonObj.getUuid(19);
}

/**
 * Obtain the device's unique identifier
 * @returns 
 */
common.getSn = function () {
    let sn = std.loadFile('/etc/.sn')
    if (sn) {
        return sn
    } else {
        return commonObj.getUuid(19);
    }
}

/**
 * Obtain the MAC address calculated from the UUID; this can be used when initializing the network card.
 * @returns Similar format: b2:a1:63:3f:99:b6
 */
common.getUuid2mac = function () {
    return commonObj.getUuid2mac(19);
}

/**
 * Get the CPU utilization rate (a number not greater than 100%).
 * @returns 
 */
common.getFreecpu = function () {
    return commonObj.getFreecpu();
}


/**
 * RSA decryption (private key encryption, public key decryption)
 * @param {ArrayBuffer} data Data to be decrypted (required)
 * @param {string} publicKey Public key (required)
 * @returns 
 */
common.arrayBufferRsaDecrypt = function (data, publicKey) {
    if (data === undefined || data === null) {
        throw new Error("dxCommon.arrayBufferRsaDecrypt:'data' parameter should not be null or empty")
    }
    if (publicKey === undefined || publicKey === null || publicKey.length < 1) {
        throw new Error("dxCommon.arrayBufferRsaDecrypt:'publicKey' parameter should not be null or empty")
    }
    return commonObj.arrayBufferRsaDecrypt(data, publicKey)
}

/**
 * @brief   String aes encryption
 */
common.aes128EcbEncrypt = function (input, key) {
    return commonObj.aes128EcbEncrypt(input, key)
}
/**
 * @brief   String aes decryption
 */
common.aes128EcbDecrypt = function (input, key) {
    return commonObj.aes128EcbDecrypt(input, key)
}

/**
 * arraybuffer ecb 128bit Pkcs5Padding aes encryption
 * @param {ArrayBuffer} input plain text
 * @param {ArrayBuffer} key Key
 * @returns ArrayBuffer Cipher
 */
common.aes128EcbPkcs5PaddingEncode = function (input, key) {
    return commonObj.aes128Pkcs7PaddingEncode(input, key)
}

/**
 * arraybuffer ecb 128bit Pkcs5Padding aes Decryption
 * 
 * @param {ArrayBuffer} input Cipher
 * @param {ArrayBuffer} key Key
 * @returns ArrayBuffer plain text
 */
common.aesEcb128Pkcs5PaddingDecode = function (input, key) {
    return commonObj.aes128Pkcs7PaddingDecode(input, key)
}

/**
 * aes ECB Pkcs5Padding 128 encryption
 * Example: common.aes128EcbPkcs5PaddingEncrypt("stamp=202008文&tic", "1234567890123456")
 * Result: ef7c3cff9df57b3bcb0951938c574f969e13ffdcc1eadad298ddbd1fb1a4d2f7
 * refer to https://www.devglan.com/online-tools/aes-encryption-decryption
 * @param {string} input  Plain text data
 * @param {string} key     Key (16-byte string)
 * @return Ciphertext hexadecimal string
 */
common.aes128EcbPkcs5PaddingEncrypt = function (input, key) {
    let data = common.hexStringToArrayBuffer(common.strToUtf8Hex(input))
    key = common.hexStringToArrayBuffer(common.strToUtf8Hex(key))
    // encryption
    let hex = common.arrayBufferToHexString(common.aes128EcbPkcs5PaddingEncode(data, key))
    return hex
}
/**
   * aes ECB Pkcs5Padding 128 Decryption
   * @param {string} input Ciphertext hexadecimal string
   * @param {string} key   Key (16-byte string)
   * @return plain text
   */
common.aes128EcbPkcs5PaddingDecrypt = function (input, key) {
    key = common.hexStringToArrayBuffer(common.strToUtf8Hex(key))
    let res = common.aesEcb128Pkcs5PaddingDecode(common.hexStringToArrayBuffer(input), key)
    return common.utf8HexToStr(common.arrayBufferToHexString(res))
}

/**
 * @brief   Stirng aes gcm encryption
 * @param {string} plainText plain text
 * @param {string} key Key
 * @returns {object} {cipherData: ArrayBuffer, key: String, iv: ArrayBuffer, tag: ArrayBuffer}
 */
common.aes128GcmEncrypt = function (plainText, key) {
    return commonObj.aes128GcmEncrypt(plainText, key)
}

/**
 * @brief   Stirng aes gcm Decryption
 * @param {ArrayBuffer} cipherData Cipher
 * @param {string} key Key
 * @param {ArrayBuffer} iv  iv
 * @param {ArrayBuffer} tag tag
 */
common.aes128GcmDecrypt = function (chiperData, key, iv, tag) {
    return commonObj.aes128GcmDecrypt(chiperData, key, iv, tag)
}

/**
 * Execute operating system commands
 * @param {*} cmd Order
 * @returns 
 */
common.system = function (cmd) {
    return commonObj.system(cmd)
}

/**
 * Execute operating system commands 
 * @param {*} cmd Commands: Commonly used operating system commands (supports most Linux commands), required.
 * @returns 
 */
common.systemBrief = function (cmd) {
    return commonObj.systemBrief(cmd)
}

/**
 * Execute operating system commands and return results.
 * @param {*} cmd Commands: Commonly used operating system commands (supports most Linux commands), required.
 * @param {*} resLen Received data length: Sometimes the returned data is very large. This value can be used to return data of a fixed length. (Required)
 * @returns 
 */
common.systemWithRes = function (cmd, resLen) {
    return commonObj.systemWithRes(cmd, resLen)
}

/**
 * Execution of operating system commands is blocked.
 * @param {*} cmd Commands: Commonly used operating system commands (supports most Linux commands), required.
 * @returns 
 */
common.systemBlocked = function (cmd) {
    return commonObj.systemBlocked(cmd)
}

/**
 * Asynchronous delayed restart
 * @param {*} delay_s Delay time
 * @returns 
 */
common.asyncReboot = function (delay_s) {
    return commonObj.asyncReboot(delay_s)
}

/**
 * bcc verification
 * @param {array} data eg:[49,50,51,52,53,54]The corresponding value is 7
 * @returns Verify the calculation results
 */
common.calculateBcc = function (data) {
    return commonObj.calculateBcc(data)
}

/**
 * CRCC validation, for example, the result of validating the string '123456' is the number 158520161.
 * @param {string} content The string data to be validated.
 * @returns 
 */
common.crc32 = function (content) {
    if (content === undefined || content === null || typeof (content) != "string" || content.length < 1) {
        throw new Error("dxCommon.crc32:'content' paramter should not be empty")
    }
    return commonObj.crc32(content)
}

/**
 * Calculate the MD5 hash. For example, the numeric array corresponding to '123456' is [49,50,51,52,53,54], and its corresponding MD5 hash is 'e10adc3949ba59abbe56e057f20f883e'.
 * However, it returns an array of numbers instead of a hexadecimal string. You can use the arrToHex function to convert it.
 * @param {array} arr Number array 
 * @returns Number array
 */
common.md5Hash = function (arr) {
    return commonObj.md5Hash(arr)
}

/**
 * Calculate the MD5 hash of a file. For example, if the content of the file is '123456', the corresponding MD5 hash is 'e10adc3949ba59abbe56e057f20f883e'.
 * However, it returns an array of numbers instead of a hexadecimal string. You can use the arrToHex function to convert it.
 * @param {string} File path, absolute path, required, usually starts with /app/code.
 * @returns Number array
 */
common.md5HashFile = function (filePath) {
    if (filePath === undefined || filePath === null || typeof (filePath) != "string") {
        return null
    }
    return commonObj.md5HashFile(filePath)
}

/**
 * Calculate HMAC MD5 encryption. For example, if the data to be encrypted is '123456' and the key is '654321', the corresponding result is '357cbe6d81a8ec770799879dc8629a53'.
 * However, both the parameters and the return value are ArrayBuffer.
 * @param {ArrayBuffer} data Content that needs to be encrypted is required.
 * @param {ArrayBuffer} key Key (required)
 * @returns ArrayBuffer
 */
common.hmacMd5Hash = function (data, key) {
    return commonObj.hmacMd5Hash(data, key)
}

/**
 * Calculate HMAC MD5 encryption. For example, if the data to be encrypted is '123456' and the key is '654321', the corresponding result is '357cbe6d81a8ec770799879dc8629a53'.
 * @param {string} data Content that needs to be encrypted is required.
 * @param {string} key Key (required)
 * @returns ArrayBuffer
 */
common.hmac = function (data, key) {
    return commonObj.hmac(data, key)
}

/**
 * Calculate the HMAC MD5 encryption for the file. For example, if the content of the file is '123456' and the key is '654321', the corresponding result is '357cbe6d81a8ec770799879dc8629a53'.
 * @param {string} filePath The file path for the encrypted content to be stored. This is an absolute path and is required. It usually starts with /app/code.
 * @param {array} key Key, array of numbers, required
 * @returns Number array
 */
common.hmacMd5HashFile = function (filePath, key) {
    return commonObj.hmacMd5HashFile(filePath, key)
}


/**
 * base64 to bin file
 * @param {string} file_path File path (required)
 * @param {string} base64Data Base64 data, required.
 * @returns 
 */
common.base64_2binfile = function (file_path, base64Data) {
    return commonObj.base64_2binfile(file_path, base64Data);
}

/**
 * Convert bin file to base64
 * @param {string} file_path File path (required)
 * @returns base64Data bBase64 data, required.
 */
common.binfile_2base64 = function (file_path) {
    return commonObj.binfile_2base64(file_path);
}

/**
 * Switch device mode
 * @description The device will restart after the mode switch and enter the specified mode. When using this method, the switching logic must be fully maintained. IDE functions cannot be used after switching to business mode.
 * @param {number} mode Note: Use (1, 2, 3) to switch modes in older versions, and use (dev, test, prod, safe) to switch modes in newer versions.
 * @returns true false
 */
common.setMode = function (mode) {
    // Note: Use (1, 2, 3) to switch between modes in older versions.
    if (mode == 1) {
        //Production mode
        commonObj.systemWithRes(`echo 'app' > /etc/.mode`, 2)
        commonObj.systemWithRes(`rm -rf /test`, 2)
    } else if (mode == 2) {
        //Debug mode
        commonObj.systemWithRes(`echo 'debug' > /etc/.mode`, 2)
        commonObj.systemWithRes(`rm -rf /test`, 2)
    } else if (mode == 3) {
        //Trial production mode
        commonObj.systemWithRes(`echo 'pp' > /etc/.mode`, 2)
    } 
    
    // Note: The new version uses (dev, test, prod, safe) to switch modes.
    else if (mode == "dev") {
        //Development Model
        commonObj.systemWithRes(`echo 'dev' > /etc/.mode_v1`, 2)
    } else if (mode == "test") {
        //Test mode (trial production mode)
        commonObj.systemWithRes(`echo 'test' > /etc/.mode_v1`, 2)
    } else if (mode == "prod") {
        //Production mode
        commonObj.systemWithRes(`echo 'prod' > /etc/.mode_v1`, 2)
    } else if (mode == "safe") {
        //Safe Mode
        commonObj.systemWithRes(`echo 'safe' > /etc/.mode_v1`, 2)
    } else {
        return false
    }
    commonObj.systemWithRes(`sync`, 2)
    commonObj.asyncReboot(2)
    return true
}

/**
 * Query device mode
 * @description Get the current device mode
 * @returns Business Model: 1, Development Model: 2, Factory Model: 28, Exception Model: -1
 */
common.getMode = function () {
    let ret = commonObj.systemWithRes(`test -e "/etc/.mode" && echo "OK" || echo "NO"`, 2)
    if (ret.includes('NO')) {
        return 28
    }
    let mode = commonObj.systemWithRes(`cat "/etc/.mode"`, 10)
    if (mode.includes('app')) {
        return 1
    } else if (mode.includes('debug')) {
        return 2
    } else {
        return -1
    }
}
/**
 * Converting a hexadecimal number to a byte array, e.g., 313233616263 -> [49, 50, 51, 97, 98, 99]
 * @param {string} str Hexadecimal strings: Lowercase hexadecimal strings with no spaces.
 * @returns Numbers
 */
common.hexToArr = function (str) {
    if (str === undefined || str === null || (typeof str) != 'string' || str.length < 1) {
        throw new Error("dxCommon.hexToArr:'str' parameter should not be empty")
    }
    let regex = /.{2}/g;
    let arr = str.match(regex);
    return arr.map(item => parseInt(item, 16));
}
/**
 * Converting a byte array to hexadecimal, e.g., [49,50,51,97,98,99] -> 313233616263
 * @param {array}numbers Number array
 * @returns str Hexadecimal strings: Lowercase hexadecimal strings with no spaces.
 */
common.arrToHex = function (numbers) {
    const hexArray = numbers.map(num => num.toString(16).padStart(2, '0').toLowerCase());
    const hexString = hexArray.join('');
    return hexString;
}
/**
 * Hexadecimal to string conversion, e.g., 313233616263 -> 123abc
 * Note that if a hexadecimal string was converted from Chinese characters, converting it back to a Chinese string will result in garbled characters because the conversion is done byte by byte.
 * @param {string} str The hexadecimal string to be converted
 * @returns 
 */
common.hexToString = function (str) {
    let regex = /.{2}/g;
    let arr = str.match(regex);
    arr = arr.map(item => String.fromCharCode(parseInt(item, 16)));
    return arr.join("");
}
// Convert the string to a UTF-8 encoded hexadecimal string.
common.strToUtf8Hex = function (str) {
    const bytes = [];
    for (let i = 0; i < str.length; i++) {
        let code = str.charCodeAt(i);
        if (code < 0x80) {
            bytes.push(code);
        } else if (code < 0x800) {
            bytes.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));
        } else if (code < 0xd800 || code >= 0xe000) {
            bytes.push(0xe0 | (code >> 12), 0x80 | ((code >> 6) & 0x3f), 0x80 | (code & 0x3f));
        } else {
            // Handling Unicode encoding
            i++;
            code = 0x10000 + (((code & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
            bytes.push(
                0xf0 | (code >> 18),
                0x80 | ((code >> 12) & 0x3f),
                0x80 | ((code >> 6) & 0x3f),
                0x80 | (code & 0x3f)
            );
        }
    }
    return this.arrToHex(bytes);
}
/**
 * Convert the received UTF-8 hexadecimal string to a string.
 * @param {string} hex 
 * @returns 
 */
common.utf8HexToStr = function (hex) {
    let array = this.hexToArr(hex)
    var out, i, len, c;
    var char2, char3;

    out = "";
    len = array.length;
    i = 0;
    while (i < len) {
        c = array[i++];
        switch (c >> 4) {
            case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
                // 0xxxxxxx
                out += String.fromCharCode(c);
                break;
            case 12: case 13:
                // 110x xxxx   10xx xxxx
                char2 = array[i++];
                out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
                break;
            case 14:
                // 1110 xxxx  10xx xxxx  10xx xxxx
                char2 = array[i++];
                char3 = array[i++];
                out += String.fromCharCode(((c & 0x0F) << 12) |
                    ((char2 & 0x3F) << 6) |
                    ((char3 & 0x3F) << 0));
                break;
        }
    }

    return out;
}
/**
 * String to hexadecimal conversion, e.g., 123abc -> 313233616263
 * @param {string} str The string to be converted
 * @returns 
 */
common.stringToHex = function (str) {
    if (str === undefined || str === null || typeof (str) != "string") {
        return null
    }
    let val = "";
    for (let i = 0; i < str.length; i++) {
        val += str.charCodeAt(i).toString(16)
    }
    return val
}

/**
 * Little-endian format to decimal number, e.g., 001001 -> 69632
 * @param {string} hexString Hexadecimal strings: Lowercase hexadecimal strings with no spaces.
 * @returns number
 */
common.littleEndianToDecimal = function (hexString) {
    // Reverse the little-endian hexadecimal string.
    let reversedHexString = hexString
        .match(/.{2}/g)  // Separated by two characters
        .reverse()  // Reverse array
        .join("");  // Merge into strings

    // Convert the reversed hexadecimal string to a decimal number.
    let decimal = parseInt(reversedHexString, 16);
    return decimal;
}


/**
 * Convert a decimal number to a hexadecimal little-endian format string.
 * eg:300->2c01
 * eg:230->e600
 * @param {number} decimalNumber Decimal number, required
 * @param {number} byteSize The number of bytes to generate. If the number of bytes exceeds the actual number of bytes, it will be padded with zeros on the right; if it is less than the actual number of bytes, it will be truncated. This field is optional and the default value is 2.
 * @returns 
 */
common.decimalToLittleEndianHex = function (decimalNumber, byteSize) {
    if (decimalNumber === undefined || decimalNumber === null || (typeof decimalNumber) != 'number') {
        throw new Error("dxCommon.decimalToLittleEndianHex:'decimalNumber' parameter should be number")
    }
    if (byteSize === undefined || byteSize === null || (typeof byteSize) != 'number' || byteSize <= 0) {
        byteSize = 2
    }
    const littleEndianBytes = [];
    for (let i = 0; i < byteSize; i++) {
        littleEndianBytes.push(decimalNumber & 0xFF);
        decimalNumber >>= 8;//Equivalent to dividing by 256
    }
    const littleEndianHex = littleEndianBytes
        .map((byte) => byte.toString(16).padStart(2, '0'))
        .join('');
    return littleEndianHex;
}

/**
 * Convert a hexadecimal string to an ArrayBuffer.
 * @param {*} hexString The hexadecimal string to be converted
 * @returns 
 */
common.hexStringToArrayBuffer = function (hexString) {
    return this.hexStringToUint8Array(hexString).buffer;
}

/**
 * Convert a hexadecimal string to a Uint8Array
 * @param {string} hexString The hexadecimal string to be converted, a lowercase hexadecimal string without spaces.
 * @returns Uint8Array object
 */
common.hexStringToUint8Array = function (hexString) {
    if (hexString === undefined || hexString === null || (typeof hexString) != 'string' || hexString.length <= 0) {
        throw new Error("dxCommon.hexStringToUint8Array:'hexString' parameter should not be empty")
    }
    let byteString = hexString.match(/.{1,2}/g);
    let byteArray = byteString.map(function (byte) {
        return parseInt(byte, 16);
    });
    let buffer = new Uint8Array(byteArray);
    return buffer;
}

/**
 * Convert ArrayBuffer to hexadecimal string format
 * @param {ArrayBuffer} buffer 
 * @returns Lowercase hexadecimal string with no spaces
 */
common.arrayBufferToHexString = function (buffer) {
    return this.uint8ArrayToHexString(new Uint8Array(buffer))
}
/**
 * Convert Uint8Array to hexadecimal string format
 * @param {Uint8Array} array 
 * @returns Lowercase hexadecimal string with no spaces
 */
common.uint8ArrayToHexString = function (array) {
    let hexString = '';
    for (let i = 0; i < array.length; i++) {
        const byte = array[i].toString(16).padStart(2, '0');
        hexString += byte;
    }
    return hexString
}
/**
 * General methods for setting/getting component handle ID
 * @param {string} name Component name (required)
 * @param {string} id Handle ID, optional
 * @param {number} pointer Handle pointer number, optional
 * @returns 
 */
common.handleId = function (name, id, pointer) {
    // Component name cannot be empty
    if (name === undefined || name === null || name === "" || typeof name !== 'string') {
        return
    }
    let map = dxMap.get('handleIds')
    // handle id
    if (id === undefined || id === null || id === "" || typeof id !== 'string') {
        id = "__" + name + "_default"
    }
    if (pointer === undefined || pointer === null || typeof pointer !== 'number') {
        // If pointer is empty, then it is retrieved.
        return map.get(id)
    } else {
        // If pointer is not null, then it is set.
        let isExist = map.get(id)
        if (isExist) {
            // The handle already exists
            return
        }
        map.put(id, pointer)
    }
}


export default common